package com.basker.pisces.domain.entity.meta;

import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import com.basker.pisces.domain.command.meta.Command;
import com.basker.pisces.domain.property.meta.Property;
import com.basker.pisces.domain.validation.meta.Validation;
import com.basker.pisces.exception.ExceptionFactory;

public class EntityHelper {

    /**
     * 通过属性名称获取子实体
     *
     * @param entity
     * @param entityName
     * @param required   是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static ChildEntity getChildEntity(AbstractEntity entity, String entityName, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(entityName, "parameter 'entityName' is required");

        Optional<ChildEntity> first = entity.getChildEntities().stream()
                .filter(e -> e.getName().equalsIgnoreCase(entityName)).findFirst();

        if (!first.isPresent() && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getChildEntity-001",
                    "entity:{0} has no child entity:{1}", entity.getName(), entityName);
        }

        return first.isPresent() ? first.get() : null;
    }

    /**
     * 通过名称获取命令
     *
     * @param entity
     * @param commandName
     * @param required    是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static Command getCommand(Entity entity, String commandName, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(commandName, "parameter 'commandName' is required");

        Command command = null;

        if (entity.getCommandMap().isEmpty()) {
            Optional<Command> optional = entity.getCommands().stream().filter(c -> c.getName().equals(commandName))
                    .findFirst();
            command = optional.isPresent() ? optional.get() : null;
        } else {
            command = entity.getCommandMap().get(commandName);
        }

        if (command == null && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getCommand-001",
                    "entity:{0} has no command:{1}", entity.getName(), commandName);
        }

        return command;
    }

    /**
     * 获取命令相关的元素的DisplayName属性的资源key,其格式为${聚合实体名称.命令名称.other}
     *
     * @param entity
     * @param command
     * @return
     */
    public static String getDisplayNameResouceKey(AbstractEntity entity, Command command) {
        Assert.notNull(entity, "parameter 'entity' isrequired");
        Assert.notNull(command, "parameter 'command' isrequired");

        Entity rootEntity = getRootEntity(entity);
        String entityKey = getResouceKeyPrefix(rootEntity);

        return genKey(entityKey, command.getName());
    }

    /**
     * 获取属性相关的元素的DisplayName属性的资源key,其格式为${聚合实体名称.属性名称.other}
     *
     * @param entity
     * @param property
     * @param other
     * @return
     */
    public static String getDisplayNameResouceKey(AbstractEntity entity, Property<?> property, @Nullable String other) {
        Assert.notNull(entity, "parameter 'entity' isrequired");
        Assert.notNull(property, "parameter 'property' isrequired");

        Entity rootEntity = getRootEntity(entity);
        String entityKey = getResouceKeyPrefix(rootEntity);

        return genKey(entityKey, property.getAlias(), other);
    }

    /**
     * 获取实体DisplayName属性的资源key,其格式为${实体名称}
     *
     * @param entity
     * @return
     */
    public static String getDisplayNameResouceKey(Entity entity) {
        Assert.notNull(entity, "parameter 'entity' isrequired");

        String entityKey = getResouceKeyPrefix(entity);
        return genKey(entityKey);
    }

    /**
     * 获取属性DisplayName属性的资源key,其格式为${所属实体名称.属性名称}
     *
     * @param property
     * @return
     */
    public static String getDisplayNameResouceKey(Property<?> property) {
        return getDisplayNameResouceKey(property, null);
    }

    /**
     * 获取属性相关的元素的DisplayName属性的资源key,其格式为${聚合实体名称.属性名称.other}
     *
     * @param property
     * @param other
     * @return
     */
    public static String getDisplayNameResouceKey(Property<?> property, @Nullable String other) {
        Assert.notNull(property, "parameter 'property' isrequired");

        return getDisplayNameResouceKey(property.getHostEntity(), property, other);
    }

    /**
     * 通过属性名称获取子实体
     *
     * @param entity
     * @param entityName
     * @param required   是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static AbstractEntity getEntity(Entity entity, String entityName, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(entityName, "parameter 'entityName' is required");

        AbstractEntity target = entity.getEntityMap().get(entityName);

        if (target == null && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getEntity-001",
                    "entity:{0} has no child entity:{1}", entity.getName(), entityName);
        }

        return target;
    }

    /**
     * 通过属性名称获取属性
     *
     * @param entity
     * @param propertyName
     * @param required     是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static Property<?> getProperty(AbstractEntity entity, String propertyName, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(propertyName, "parameter 'propertyName' is required");

        Optional<Property<?>> first = entity.getProperties().stream()
                .filter(e -> e.getName().equalsIgnoreCase(propertyName)).findFirst();

        if (!first.isPresent() && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getProperty-001",
                    "entity:{0} has no property:{1}", entity.getName(), propertyName);
        }

        return first.isPresent() ? first.get() : null;
    }

    /**
     * 通过别名获取属性
     *
     * @param entity
     * @param propertyAlias
     * @param required      是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static Property<?> getPropertyByAlias(Entity entity, String propertyAlias, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(propertyAlias, "parameter 'propertyAlias' is required");

        Property<?> property = entity.getPropertyMap().get(propertyAlias);

        if (property == null && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getPropertyByAlias-001",
                    "entity:{0} has no property:{1}", entity.getName(), propertyAlias);
        }

        return property;
    }

    public static Entity getRootEntity(AbstractEntity entity) {
        Assert.notNull(entity, "parameter 'entity' is required");

        if (entity instanceof Entity) {
            return (Entity) entity;
        }

        if (entity instanceof ChildEntity) {
            return getRootEntity(((ChildEntity) entity).getParentEntity());
        }

        throw new IllegalArgumentException("unknow entity type:" + entity.getClass());
    }

    /**
     * 通过名称获取验证对象
     *
     * @param entity
     * @param validationName
     * @param required       是否是必须，如果为true但结果没有找到会抛出异常
     * @return
     */
    public static Validation getValidation(Entity entity, String validationName, boolean required) {
        Assert.notNull(entity, "parameter 'entity' is required");
        Assert.hasText(validationName, "parameter 'validationName' is required");

        Validation validation = null;

        if (entity.getValidationMap().isEmpty()) {
            Optional<Validation> optional = entity.getValidations().stream()
                    .filter(v -> v.getName().equals(validationName)).findFirst();
            validation = optional.isPresent() ? optional.get() : null;
        } else {
            validation = entity.getValidationMap().get(validationName);
        }

        if (validation == null && required) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.EntityHelper.getValidation-001",
                    "entity:{0} has no validation:{1}", entity.getName(), validationName);
        }

        return validation;
    }

    private static String genKey(String... keys) {
        keys = Stream.of(keys).filter(k -> k != null).toArray(l -> new String[l]);

        StringBuilder keyBuilder = new StringBuilder();

        keyBuilder.append("${");
        keyBuilder.append(String.join(".", keys));
        keyBuilder.append("}");

        return keyBuilder.toString();
    }

    private static String getResouceKeyPrefix(Entity entity) {
        return entity.getFullEntityName();
    }
}
